/*
 * 教材中的示例程序之完整版.
 * 书名：《编译原理基础》
 * 版次：第三版
 * 作者：王献青，张立勇，张淑平，刘坚
 * 出版社：西安电子科技大学出版社
 * 程序完成时间：2025.03
 */

//
// 文件内容：语义处理程序的实现
//
// 该程序用于支持教材中的程序清单4.1，包括
// (1) 为表达式生成语法树，
// (2) 对表达式求值，以遍历语法树的方法实现，
// (3) 输出语法树。
//

#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <stdbool.h>

#include "semantics.h"

static bool  printTraceInfo = false;
void set_trace(bool is_print) {
    printTraceInfo = is_print;
}
bool is_trace() {
    return printTraceInfo;
}

static void semantic_error(const char * msg, ...) {
    va_list  ap;
    va_start(ap, msg);
    fprintf(stdout, "semantic-error: ");
    vfprintf(stdout, msg, ap);
    fprintf(stdout, "\n");
    va_end(ap);
}


// 创建一个运算对应的语法树结点，
// 该运算的两个操作数，分别表示为其左右孩子为根的子树
Node_Ptr makeNode(TokenKind op, Node_Ptr left, Node_Ptr right)
{
    Node_Ptr parent = (Node_Ptr)calloc(1, sizeof(Node));
    parent->token = op;
    parent->left  = left;
    parent->right = right;
    return  parent;
}

// 创建叶子结点，它表示 ID 或 NUM 形成的表达式
Node_Ptr makeLeaf(TokenKind op, const char * text)
{
    Node_Ptr leaf = (Node_Ptr)calloc(1, sizeof(Node));
    leaf->token = op;
    leaf->left  =  leaf->right = NULL;
    if ( op == ID ) {
        // 考虑到文法 G3.9" 中没有赋值结构，即
        // 无法为表达式中的 id 指定一个数值，所以固定填写为 1.
        leaf->numValue = 1;
    } else {  // NUM
        leaf->numValue = strtod(text, NULL);
    }
    return  leaf;
}


// 计算表达式的值： 遍历树 + 求值
double   computeValue(Node_Ptr root)
{
    if (NULL == root) return 0;

    if (root->token == ID || root->token == NUM)
        return root->numValue;

    double result;
    double left  = computeValue(root->left);
    double right = computeValue(root->right);
    switch (root->token) {
        case PLUS:     result = left + right;  break;
        case MINUS:    result = left - right;  break;
        case MUL:      result = left * right;  break;
        case DIV:
            if ( right == 0.0 ) {
                semantic_error("divided by 0");
                result = 1;
            } else
                result = left / right;
            break;
        case MOD:
            if ( right == 0.0 ) {
                semantic_error("divided by 0");
                result = 1;
            } else
                result = fmod(left, right);
            break;
        default:
            result = 1;
            break;
    }

    return root->numValue = result;
}


static void printTree_impl(Node_Ptr root, int depth, FILE* fp)
{
    if (NULL == root) return;

    for(int i=0; i<depth; i+=1)
        fprintf(fp, "  ");
    fprintf(fp, "%-10s    %.2lf\n",
            lookup_kindname(root->token),
            root->numValue);

    printTree_impl(root->left, depth + 1, fp);
    printTree_impl(root->right, depth + 1, fp);
}


//print the Abstract-Syntax-Tree(AST) of an expression
void  printTree(Node_Ptr root)
{
    fprintf(stdout, "expr_AST:\n");
    printTree_impl(root, 1, stdout);
}
